﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using PServiceBus.Core.Provider;
using Phoenix.ReflectionExtensions;
using System.Collections;
using PServiceBus.Core.Runtime.Serializers;
using PServiceBus.Core.Runtime.Extensions;
using PServiceBus.Core.Runtime.Transports;

namespace PServiceBus.Core.Runtime.Messages {
    public sealed class MessageMapper {
        private readonly static MethodInfo _transportProviderMessageTo = typeof(TransportProvider).GetMethod("MessageTo",
            BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static);
        private Type _messageType;
        private IEnumerable<object> _messages = null;
        private Dictionary<string, object> _messageStructure;
        private bool _excludeXmlItemsAttribute;

        private MessageMapper() { }

        public static MessageMapper Map(string message, Dictionary<string, object> messageStructure = null, bool excludeXmlItemsAttribute = false) {
            var messageMapper = new MessageMapper() { _messageStructure = messageStructure, _excludeXmlItemsAttribute = excludeXmlItemsAttribute };
            messageMapper.TranslateMessage(message);
            return messageMapper;
        }

        public IEnumerable<MessageMapperToken> Tokens {
            get {
                foreach (var message in _messages) 
                    yield return new MessageMapperToken(message, _excludeXmlItemsAttribute);
            }
        }

        private void TranslateMessage(string message) {
            var xmlTransformation = TransportProvider.GetTransformation(TransportFormat.Xml);
            if (_messageStructure != null) {
                var xmlJson = JsonSerializer.XmlToJson(xmlTransformation.Transform(_messageStructure), includeDictTypeInfo: true);
                var mapping = xmlJson.FromJson() as Dictionary<string, object>;
                _messageType = xmlTransformation.ToCanonicalType(mapping);
            } else {
                //Assuming if message structure is not provided that message will always be xml format
                //_messageType = xmlTransformation.GetCanonicalType(message);
                var format = TransportProvider.GetMessageFormat(message);
                var transformation = TransportProvider.GetTransformation(format);
                _messageType = transformation.GetCanonicalType(message);
            }
            var messageToMethod = _transportProviderMessageTo.MakeGenericMethod(_messageType);
            _messages = (messageToMethod.Invoke(null, new[] { message }) as IEnumerable).Cast<object>();
        }
    }
}
